'''
Created on Mar 10, 2010

@author: sternron
'''

from webgraphs.onlineClosestCliqueSearch import OnlineClosestCliqueSearch
import logging
import string
class OnlineOngoingCliqueSearch(OnlineClosestCliqueSearch):
    '''
    An online crawler that continues to run even after a k-clique has been found
    '''


    def __init__(self,clique_algorithm):
        OnlineClosestCliqueSearch.__init__(self,clique_algorithm)
        clique_algorithm.add_member_listener(self)
        self.found_members = set()
        self.filtered_members = set()
        self.member_found = False
    
    def export_state(self,config_file_name):
        ''' Export the entire state of the search '''
        OnlineClosestCliqueSearch.export_state(self, config_file_name)
        
        members_file_name = "%s.members.txt" % config_file_name
        members_file = file(members_file_name,'w')
        for node in self.found_members:
            members_file.write("%d\t%s\n" % (node, self.node_to_title[self.nodes[node]]))
        members_file.close()
        
        config_file = file(config_file_name,'a')
        config_file.write("clique_members_file=%s\n" % members_file_name)
        config_file.close()
        
        
    def import_state(self, config_file_name):
        ''' Import the entire state of the search from files, and update algorithm state accordingly '''        
        
        config = self.import_config(config_file_name)
        
        logging.info("Loading found clique members from file...")
        self.found_members = set()      
        members_file = open(config['clique_members_file'], 'r')
        for line in members_file:
            line = line.strip()
            parts = line.split('\t')
            host_name = parts[1]
            node_index = int(parts[0])
            self.found_members.add(node_index)
        members_file.close()
        
        OnlineClosestCliqueSearch.import_state(self, config_file_name)
    
#    def is_ascii(s):
#    return all(ord(c) < 128 for c in s)
        
    def notify_member_found(self,a_clique):
        ''' An event that is raised when a new k-clique has been found '''
        logging.info(" --- Found new member! --- ")
        self.member_found=True
        
        
    def search(self, exploration_limit):
        ''' Search for the desired k-clique, starting from the given current node index
        and until the clique is found or the exploration limit has been found'''      
        while ((self.algorithm.halt() == False) and (self.algorithm.iteration <= exploration_limit)):
            current_node_index = self.algorithm.choose_node()
            self.exploration_cycle(current_node_index)
            
            # Update found_members with non-root nodes
            if self.member_found:
                algorithm_members = set(self.algorithm.found_members)
                new_members = algorithm_members.difference(self.found_members)
                while len(new_members)>0:
                    for node in new_members:
                        self.found_members.add(node)
                        if node not in self.algorithm.expanded:
                            self.exploration_cycle(node)
                        
                        member_title = self.node_to_title[self.nodes[node]]
                        logging.info("Found new member [%d]%s!" % (node, member_title))
                        if self.title_not_in_english(member_title):
                            logging.info("Member not in english ==> ignored")
                            self.filtered_members.add(node)
                        else:    
                            # If reached maximum members - halt
                            if 'required_members' in dir(self):
                                if self.required_members<=len(self.found_members)-len(self.filtered_members):
                                    logging.info("----------------- FOUND ALL MEMBERS!!! ------------- ")
                                    return
                                
                            if self.algorithm.iteration >= exploration_limit:
                                logging.info("Exploration limit reached")
                                return
                    # Check if the exploration of the members uncovered new members
                    algorithm_members = set(self.algorithm.found_members)
                    new_members = algorithm_members.difference(self.found_members)
                                        
                self.member_found=False

    def title_not_in_english(self,to_check):
        ''' Checks if a string is not in english. This is only a heuristic '''
        valid_letters = string.ascii_letters+"'()[]/\\: &-=0123456789?.!+"
        counter = 0        
        for check_char  in to_check:
            if check_char not in valid_letters:
                counter=counter+1
                if counter>5:
                    return True
        return False
    
    def set_required_members(self,pRequired_members):
        self.required_members = pRequired_members
                 
    def exploration_cycle(self, node):
        ''' A full exploration cycle, including the online exploration and updating the algorithm '''
        self.algorithm.current_node = node
        self.explore(node)
        self.algorithm.expand(node)
        self.algorithm.iteration = self.algorithm.iteration+1
